
package physiqueEngine;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.AbstractAction;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;

import physiqueEngine.formes.Oval;
import physiqueEngine.formes.PhysBody;
import physiqueEngine.formes.Rectangle;
import physiqueEngine.graphs.Graph;
import physiqueEngine.gui.LabeledField;
import physiqueEngine.gui.StatusBar;
import physiqueEngine.thread.TimeManager;

/**
 * @author Jean-Simon Garneau
 * @author David Bernard
 * 
 */
public class TestGUI extends JFrame implements Refreshable, TimeManager, MouseMotionListener
{
    private static final long DEF_PERIOD = 15;
    private static final int HEIGHT = 500;
    private static final int WIDTH = 500;

    private WorldPanel scene = null;
    private Timer frameTimer = null;
    private JScrollPane scrollPane = null;

    private Point currentMouseLocation = null;

    // Declaration
    private ControlPanel west = null;
    private JPanel east = null;
    private JToolBar toolBar = null;

    // Les graphs
    private Graph speedGraph = null;
    private Graph accelGraph = null;

    // Actions
    private AbstractAction addBodyACT = null;
    private AbstractAction startACT = null;
    private AbstractAction stopACT = null;
    private AbstractAction tickACT = null;
    private AbstractAction purgeACT = null;
    private AbstractAction circlesACT = null;
    private AbstractAction rectanglesACT = null;
    private AbstractAction trianglesACT = null;
    private AbstractAction testCollACT = null;

    private StatusBar statusBar = null;

    private int shapeSwitch = 0;
    private long frameTick = 0;
    private long period = 0;

    public TestGUI()
    {
        this.scene = new WorldPanel();
        this.scene.addMouseMotionListener(this);
        this.addKeyListener(this.scene);
        this.currentMouseLocation = new Point();

        // Set des parametres par defauts.
        this.setPeriod(TestGUI.DEF_PERIOD);

        // Setting de JFrame.
        this.setTitle("Physics Test");

        this.setSize(TestGUI.WIDTH, TestGUI.HEIGHT);
        this.setExtendedState(JFrame.MAXIMIZED_BOTH);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Initialisations.
        this.initActions();
        this.initComponents();
    }

    private void initActions()
    {
        // Buttons & Actions
        this.addBodyACT = new AddBodyAction();
        this.purgeACT = new PurgeAction();
        this.startACT = new StartAction();
        this.stopACT = new StopAction();
        this.tickACT = new TickAction();
        
        // Pour test
        this.testCollACT = new TestColAction();

        this.circlesACT = new AbstractAction("Circle")
        {
            @Override
            public void actionPerformed(ActionEvent arg0)
            {
                TestGUI.this.shapeSwitch = 0;
            }
        };

        this.rectanglesACT = new AbstractAction("Rectangle")
        {
            @Override
            public void actionPerformed(ActionEvent arg0)
            {
                TestGUI.this.shapeSwitch = 1;
            }
        };

        this.trianglesACT = new AbstractAction("Triangle")
        {
            @Override
            public void actionPerformed(ActionEvent arg0)
            {
                TestGUI.this.shapeSwitch = 2;
            }
        };
    }

    private void initComponents()
    {
        // Creation des Panels
        this.west = new ControlPanel();
        this.statusBar = new StatusBar(this);
        this.scrollPane = new JScrollPane(this.scene);
        this.toolBar = new JToolBar();

        // config du JToolBar
        this.toolBar.setFloatable(false);
        this.toolBar.add(this.addBodyACT);
        this.toolBar.add(this.purgeACT);
        this.toolBar.addSeparator();
        this.toolBar.add(this.startACT);
        this.toolBar.add(this.stopACT);
        this.toolBar.add(this.tickACT);
        this.toolBar.addSeparator();
        this.toolBar.add(this.testCollACT);

        // config du GraphPanel
        this.east = new JPanel();
        this.east.setLayout(new GridLayout(2, 1));
        this.speedGraph = new Graph();
        scene.speedGraph = this.speedGraph;
        this.accelGraph = new Graph();
        this.east.add(this.speedGraph);
        this.east.add(this.accelGraph);

        // Add panels to Window
        this.add(this.scrollPane, BorderLayout.CENTER);
        this.add(this.west, BorderLayout.WEST);
        this.add(this.east, BorderLayout.EAST);
        this.add(this.statusBar, BorderLayout.SOUTH);
        this.add(this.toolBar, BorderLayout.NORTH);
    }

    public Oval createOval()
    {
        return this.scene.addOval();
    }

    public Rectangle createRect()
    {
        return this.scene.addRectangle();
    }

    public Point2D.Double getPosXYValue()
    {
        return new Point2D.Double(this.getPosXValue(), this.getPosYValue());
    }

    public double getPosXValue()
    {
        return Double.parseDouble(this.west.posXField.getFieldText());
    }

    public double getPosYValue()
    {
        return Double.parseDouble(this.west.posYField.getFieldText());
    }

    public double getHeightValue()
    {
        return Double.parseDouble(this.west.heihtField.getFieldText());
    }

    public double getWidthValue()
    {
        return Double.parseDouble(this.west.widthField.getFieldText());
    }

    public double getXValue()
    {
        return Double.parseDouble(this.west.posXField.getFieldText());
    }

    public double getYValue()
    {
        return Double.parseDouble(this.west.posYField.getFieldText());
    }

    public double getSpeedValue()
    {
        return Double.parseDouble(this.west.speedField.getFieldText());
    }

    public float getDirValue()
    {
        return Float.parseFloat(this.west.dirField.getFieldText());
    }

    public Point getMouseLocation()
    {
        return this.currentMouseLocation;
    }

    public String getMouseLocationString()
    {
        return "(" + this.currentMouseLocation.x + ", " + this.currentMouseLocation.y + ")";
    }

    public double getVit()
    {
        return Double.parseDouble(this.west.speedField.getFieldText());
    }

    @Override
    public void setVisible(boolean visible)
    {
        this.setTicking(visible);
        super.setVisible(true);
    }

    @Override
    public boolean setPeriod(long period)
    {
        boolean ok = true;

        if (ok)
        {
            this.period = period;
        }

        return ok;
    }

    @Override
    public long getPeriod()
    {
        return this.period;
    }

    public long getTickCount()
    {
        return this.frameTick;
    }

    @Override
    public boolean isTicking()
    {
        return this.frameTimer != null;
    }

    @Override
    public void setTicking(boolean ticking)
    {
        if (ticking)
        {
            this.frameTimer = new Timer();
            this.frameTimer.schedule(new FrameUpdate(), 0, this.getPeriod());
        }
        else
        {
            this.frameTimer.cancel();
            this.frameTimer = null;
        }
    }

    @Override
    public void refresh()
    {
        this.statusBar.refresh();
        this.scene.repaint();
    }

    @Override
    public void mouseMoved(MouseEvent e)
    {
        this.currentMouseLocation = e.getPoint();
    }

    @Override
    public void mouseDragged(MouseEvent e)
    {
    }

    public boolean getFixedBoxValue()
    {
        return this.west.fixedBox.isSelected();
    }

    public long getWorldTick()
    {
        return this.scene.getTickCount();
    }

    public int getBodyCount()
    {
        return this.scene.getBodyCount();
    }

    public long getWorldPeriod()
    {
        return this.scene.getPeriod();
    }

    private class FrameUpdate extends TimerTask
    {
        @Override
        public void run()
        {
            TestGUI.this.frameTick++;
            TestGUI.this.refresh();
        }
    }

    private class TickAction extends AbstractAction
    {
        public TickAction()
        {
            super("Tick");
        }

        @Override
        public void actionPerformed(ActionEvent arg0)
        {
            if (!TestGUI.this.scene.isTicking())
            {
                TestGUI.this.scene.refresh();
            }
        }
    }

    private class TestColAction extends AbstractAction
    {
        public TestColAction()
        {
            super("TestColl");
        }

        @Override
        public void actionPerformed(ActionEvent arg0)
        {
            PhysBody bodyA = null;
            PhysBody bodyB = null;

            bodyA = TestGUI.this.createOval();
            bodyB = TestGUI.this.createOval();

            bodyA.setLocation(300, 300);
            bodyA.setDimension(30, 30);
            bodyA.setSpeed(5, 30f);

            bodyB.setLocation(400, 300);
            bodyB.setDimension(30, 30);
            bodyB.setSpeed(5, 130f);
        }
    }

    private class PurgeAction extends AbstractAction
    {
        public PurgeAction()
        {
            super("Purge");
        }

        @Override
        public void actionPerformed(ActionEvent arg0)
        {
            TestGUI.this.scene.purgeShapes();
        }
    }

    private class StartAction extends AbstractAction
    {
        private StartAction()
        {
            super("Start");
        }

        @Override
        public void actionPerformed(ActionEvent arg0)
        {
            if (!TestGUI.this.scene.isTicking())
            {
                TestGUI.this.scene.setTicking(true);
            }
        }
    }

    private class StopAction extends AbstractAction
    {
        private StopAction()
        {
            super("Stop");
        }

        @Override
        public void actionPerformed(ActionEvent arg0)
        {
            if (TestGUI.this.scene.isTicking())
            {
                TestGUI.this.scene.setTicking(false);
            }
        }
    }

    private class AddBodyAction extends AbstractAction
    {
        public AddBodyAction()
        {
            super("Add Body");
        }

        @Override
        public void actionPerformed(ActionEvent arg0)
        {
            int shapeType = TestGUI.this.shapeSwitch;
            PhysBody body = null;

            try
            {
                // Oval
                if (shapeType == 0)
                {
                    body = TestGUI.this.createOval();
                }

                // Rect
                else if (shapeType == 1)
                {
                    body = TestGUI.this.createRect();
                }

                // Triangle
                else if (shapeType == 2)
                {

                }
                else
                {

                }

                body.setLocation(TestGUI.this.getXValue(), TestGUI.this.getYValue());
                body.setDimension(TestGUI.this.getHeightValue(), TestGUI.this.getWidthValue());
                body.setSpeed(TestGUI.this.getSpeedValue(), TestGUI.this.getDirValue());
                body.setFix(TestGUI.this.getFixedBoxValue());

                TestGUI.this.scene.repaint();
            }
            catch (NumberFormatException e)
            {
                System.out.println("Wrong param");
            }
        }
    }

    private class ControlPanel extends JPanel
    {
        private GridBagConstraints gridBagConstr = null;
        private ButtonGroup shapesGroups = null;
        private JCheckBox fixedBox = null;

        private JPanel westNorth = null;
        private JPanel westCenter = null;

        private LabeledField heihtField = null;
        private LabeledField widthField = null;
        private LabeledField posXField = null;
        private LabeledField posYField = null;
        private LabeledField dirField = null;
        private LabeledField speedField = null;

        private int gridBagPosition = 0;

        private ControlPanel()
        {
            this.setLayout(new BorderLayout());

            this.initNorth();
            this.initCenter();

            // Pour test
            this.heihtField.setFieldText("30");
            this.widthField.setFieldText("30");
            this.posXField.setFieldText("300");
            this.posYField.setFieldText("300");
            this.dirField.setFieldText("40");
            this.speedField.setFieldText("5");
        }

        public void addField(LabeledField lField)
        {
            // Ajout de l'étiquette
            this.gridBagConstr.weightx = 0.0;
            this.gridBagConstr.gridx = 0;
            this.gridBagConstr.gridy = this.gridBagPosition;
            this.westCenter.add(lField.getLabel(), this.gridBagConstr);

            // Ajout du champ
            this.gridBagConstr.weightx = 1.0;
            this.gridBagConstr.gridx = 1;
            this.gridBagConstr.gridy = this.gridBagPosition;
            this.westCenter.add(lField.getField(), this.gridBagConstr);

            this.gridBagPosition++;
        }

        private void initNorth()
        {
            JToggleButton circles = null;
            JToggleButton rectangles = null;
            JToggleButton triangles = null;

            this.westNorth = new JPanel(new FlowLayout(1));

            circles = new JToggleButton(circlesACT);
            rectangles = new JToggleButton(rectanglesACT);
            triangles = new JToggleButton(trianglesACT);
            circles.setSelected(true);

            this.shapesGroups = new ButtonGroup();
            this.shapesGroups.add(circles);
            this.shapesGroups.add(rectangles);
            this.shapesGroups.add(triangles);

            this.westNorth.add(circles);
            this.westNorth.add(rectangles);
            this.westNorth.add(triangles);

            this.add(this.westNorth, BorderLayout.NORTH);
        }

        private void initCenter()
        {
            this.westCenter = new JPanel(new GridBagLayout());

            // Création du gridBagConstraint.
            this.gridBagConstr = new GridBagConstraints();
            this.gridBagConstr.fill = GridBagConstraints.HORIZONTAL;
            this.gridBagConstr.insets = new Insets(1, 5, 1, 5);
            this.gridBagConstr.anchor = GridBagConstraints.NORTH;

            // Création des champs
            this.heihtField = new LabeledField(" Height :");
            this.widthField = new LabeledField(" Width :");
            this.posXField = new LabeledField(" Position X :");
            this.posYField = new LabeledField(" Position Y :");
            this.dirField = new LabeledField(" Direction (Degré) :");
            this.speedField = new LabeledField(" Vitesse :");

            // Ajout des champs
            this.addField(this.heihtField);
            this.addField(this.widthField);
            this.addField(this.posXField);
            this.addField(this.posYField);
            this.addField(this.dirField);
            this.addField(this.speedField);

            // Ajout du checkBox
            this.fixedBox = new JCheckBox("Fixed");
            this.gridBagConstr.weightx = 1.0;
            this.gridBagConstr.gridx = 1;
            this.gridBagConstr.gridy = this.gridBagPosition++;
            this.westCenter.add(this.fixedBox, this.gridBagConstr);

            this.add(this.westCenter, BorderLayout.CENTER);
        }
    }

    public static void main(String[] args)
    {
        TestGUI win = new TestGUI();

        win.setVisible(true);
    }
}
